jProton = function(path, root){
  return new jProton.collection().select(path, root);
};

window.$ = jProton;

jProton.collection = function(){};
jProton.collection.prototype = {
  e: new Array()
}

/**
 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
 * @param {String/HTMLElement/Array} el An element id, element or array of elements
 * @param {String} selector The simple selector to test
 * @return {Boolean}
 */
jProton.collection.prototype.is = function(el, ss){
  if(typeof el == "string")
    el = document.getElementById(el);

  var isArray = (el instanceof Array);
  var result = this.filter(isArray ? el : [el], ss);
  return isArray ? (result.length == el.length) : (result.length > 0);
};

jProton.selector = {
  cache: {}, simpleCache: {},
  reTrim: /^\s+|\s+$/g,
  reTemplate: /\{(\d+)\}/g,
  reMode: /^(\s?[\/>+~]\s?|\s|$)/,
  reSimple: /^(#)?([\w-\*]+)/,
  reNth: /(\d*)n\+?(\d*)/, 
  reNth2: /\D/,
  key: 30803,
  isIE: window.ActiveXObject ? true : false,

  /**
   * Collection of matching regular expressions and code snippets.
   */
  matchers: [{
      re: /^\.([\w-]+)/,
      select: 'e = this.byClass(e, " {1} ");'
    }, {
      re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
      select: 'e = this.byPseudo(e, "{1}", "{2}");'
    },{
      re: /^\[([\w-]+)(\S?=)?(\S+|["'][^"']["'])?\]/,
      select: 'e = this.byAttr(e, "{1}", "{2}", "{3}");'
    }, {
      re: /^#([\w-]+)/,
      select: 'e = this.byId(e, "{1}");'
    },{
      re: /^@([\w-]+)/,
      select: 'return {firstChild:{nodeValue:attrValue(e, "{1}")}};'
    }
  ],
  
  /**
   * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
   * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
   */
  operators: {
    "=" : function(a, v){
      return a == v;
    },
    "!=" : function(a, v){
      return a != v;
    },
    "^=" : function(a, v){
      return a && a.substr(0, v.length) == v;
    },
    "$=" : function(a, v){
      return a && a.substr(a.length-v.length) == v;
    },
    "*=" : function(a, v){
      return a && a.indexOf(v) !== -1;
    },
    "%=" : function(a, v){
      return (a % v) == 0;
    },
    "|=" : function(a, v){
      return a && (a == v || a.substr(0, v.length+1) == v+'-');
    },
    "~=" : function(a, v){
      return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
    }
  },
  
  /**
   * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
   * and the argument (if any) supplied in the selector.
   */
  pseudos: {
    "first-child" : function(e){
      var r = new Array(), ri = -1, n;
      for(var i = 0, ei; ei = n = e[i]; i++){
        while((n = n.previousSibling) && n.nodeType != 1);
        if(!n)
          r[++ri] = ei;
      }
      return r;
    },
  
    "last-child" : function(e){
      var r = new Array(), ri = -1, n;
      for(var i = 0, ei; ei = n = e[i]; i++){
        while((n = n.nextSibling) && n.nodeType != 1);
        if(!n)
          r[++ri] = ei;
      }
      return r;
    },
  
    "nth-child" : function(e, a) {
      var r = new Array(), ri = -1;
      var m = this.reNth.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !this.reNth2.test(a) && "n+" + a || a);
      var f = (m[1] || 1) - 0, l = m[2] - 0;
      for(var i = 0, n; n = e[i]; i++){
        var pn = n.parentNode;
        if (batch != pn._batch) {
          var j = 0;
          for(var cn = pn.firstChild; cn; cn = cn.nextSibling)
            if(cn.nodeType == 1)
               cn.nodeIndex = ++j;
  
          pn._batch = batch;
        }
        if (f == 1) {
          if (l == 0 || n.nodeIndex == l)
            r[++ri] = n;
  
        } else if ((n.nodeIndex + l) % f == 0)
          r[++ri] = n;
      }
      return r;
    },
  
    "only-child" : function(e){
      var r = new Array(), ri = -1;;
      for(var i = 0, ei; ei = e[i]; i++)
        if(!prev(ei) && !next(ei))
          r[++ri] = ei;
  
      return r;
    },
  
    "empty" : function(e){
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++){
        var cns = ei.childNodes, j = 0, cn, empty = true;
        while(cn = cns[j]){
          ++j;
          if(cn.nodeType == 1 || cn.nodeType == 3){
            empty = false;
            break;
          }
        }
        if(empty)
          r[++ri] = ei;
      }
      return r;
    },
  
    "contains" : function(e, v){
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++)
        if((ei.textContent||ei.innerText||'').indexOf(v) != -1)
          r[++ri] = ei;
  
      return r;
    },
  
    "nodeValue" : function(e, v){
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++)
        if(ei.firstChild && ei.firstChild.nodeValue == v)
          r[++ri] = ei;
  
      return r;
    },
  
    "checked" : function(e){
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++)
        if(ei.checked == true)
          r[++ri] = ei;
  
      return r;
    },
  
    "not" : function(e, simple){
      return jProton.selector.filter(e, simple, true);
    },
  
    "any" : function(e, selectors){
      var ss = selectors.split('|');
      var r = new Array(), ri = -1, s;
      for(var i = 0, ei; ei = e[i]; i++)
        for(var j = 0; s = ss[j]; j++)
          if(this.is(ei, s)){
            r[++ri] = ei;
            break;
          }
  
      return r;
    },
  
    "odd" : function(e){
      return this["nth-child"](e, "odd");
    },
  
    "even" : function(e){
      return this["nth-child"](e, "even");
    },
  
    "nth" : function(e, a){
      return e[a-1] || new Array();
    },
  
    "first" : function(e){
      return e[0] || new Array();
    },
  
    "last" : function(e){
      return e[e.length-1] || new Array();
    },
  
    "has" : function(e, simple){
      var s = this.select;
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++)
        if(s(simple, ei).length > 0)
          r[++ri] = ei;
  
      return r;
    },
  
    "next" : function(e, simple){
      var is = this.is;
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++){
        var n = next(ei);
        if(n && is(n, simple))
          r[++ri] = ei;
      }
      return r;
    },
  
    "prev" : function(e, simple){
      var is = this.is;
      var r = new Array(), ri = -1;
      for(var i = 0, ei; ei = e[i]; i++){
        var n = prev(ei);
        if(n && is(n, simple))
          r[++ri] = ei;
      }
      return r;
    }
  },

  quickId: function(id){
    return [document.getElementById(id)];
  },
  
  byId: function(e, id){
    var r = new Array(), ri = -1;
    for(var i=0, ei; ei=e[i]; i++)
      if(ei && ei.id == id)
        r[++ri] = ei;
        return r;
  
    return r;
  },
  
  byTag: function(e, name){
    var r=new Array(), ri=-1, n=name.toLowerCase();
    for(var i=0, ei; ei=e[i]; i++)
      if(ei.nodeType == 1 && ei.tagName.toLowerCase()==n)
        r[++ri] = ei;
  
    return r;
  },
  
  byClass: function(e, name){
    var r=new Array(), ri=-1;
    for(var i=0, ei; ei=e[i]; i++)
      if((' '+ei.className+' ').indexOf(name) != -1)
        r[++ri] = ei;
  
    return r;
  },
  
  byAttr: function(e, attr, op, val){
    var r=new Array(), ri=-1, a, f=this.operators[op];
    for(var i=0, ei; ei=e[i]; i++) {
      a = ei.getAttribute(attr);
      if((f && f(a, val)) || (!f && a))
        r[++ri] = ei;
    }
  
    return r;
  },
  
  byPseudo: function(e, name, val){
    return this.pseudos[name](e, val);
  },
  
  getNodes: function(e, mode, name){
    var r=new Array(), ri=-1, cs;
    if(!e)
      return r;
  
    name = name || "*";
    if(typeof e.getElementsByTagName != "undefined")
      e = [e];
  
    if(!mode){
      for(var i = 0, ei; ei = e[i]; i++){
        cs = ei.getElementsByTagName(name);
        for(var j = 0, ci; ci = cs[j]; j++)
          r[++ri] = ci;
      }
    }else if(mode == "/" || mode == ">"){
      var utag = name.toUpperCase();
      for(var i = 0, ei, cn; ei = e[i]; i++){
        cn = ei.children || ei.childNodes;
        for(var j = 0, cj; cj = cn[j]; j++)
          if(cj.nodeName == utag || cj.nodeName == name  || name == '*')
            r[++ri] = cj;
      }
    }else if(mode == "+"){
      var utag = name.toUpperCase();
      for(var i = 0, ei; ei = e[i]; i++){
        while((ei = ei.nextSibling) && ei.nodeType != 1);
        if(ei && (ei.nodeName == utag || ei.nodeName == name || name == '*'))
          r[++ri] = ei;
      }
    }else if(mode == "~"){
      for(var i = 0, ei; ei = e[i]; i++){
        while((ei = ei.nextSibling) && (ei.nodeType != 1 || (name == '*' || ei.tagName.toLowerCase()!=name)));
        if(ei)
          r[++ri] = ei;
      }
    }
    return r;
  },
  
  next: function(n){
    while((n = n.nextSibling) && n.nodeType != 1);
    return n;
  },
  
  prev: function(n){
    while((n = n.previousSibling) && n.nodeType != 1);
    return n;
  },
  
  concat: function(a, b){
    if(b.slice)
      return a.concat(b);
  
    for(var i=0, l=b.length, al=a.length; i<l; i++)
      a[al++] = b[i];
  
    return a;
  },
  
  unique: function(e){
    if(!e)
      return new Array();
  
    var len = e.length, ei, i, r = e, ej, ri = -1;
    if(!len || typeof e.nodeType != "undefined" || len == 1)
      return e;
  
    // uniqueIEXml
    var selector = jProton.selector, d = ++selector.key;
    if(selector.isIE && typeof e[0].selectSingleNode != "undefined") {
      e[0].setAttribute("__unique", d);
      var r = [e[0]];
      for(var i=1, rl=1; ei=e[i]; i++)
        if(!ei.getAttribute("__unique") != d){
          ei.setAttribute("__unique", d);
          r[rl++] = ei;
        }
      for(var i=1; ei=e[i]; i++)
        ei.removeAttribute("__unique");
    
      return r;
    }
  
    e[0].__unique = d;
    for(i=1; ei=e[i]; i++){
      if(ei.__unique != d)
        ei.__unique = d
      else{
        r = new Array();
        for(var j=0; j<i; j++)
          r[++ri] = e[j];
        for(j=i+1; ej=e[j]; j++){
          if(ej.__unique != d){
            ej.__unique = d;
            r[++ri] = ej;
          }
        }
        return r;
      }
    }
    return r;
  },
  
  diff: function(e1, e2){
    var l1 = e1.length;
    if(!l1)
      return e2;
  
    var d = ++jProton.selector.key, r=new Array(), ei;
    // quickDiffIEXml
    if(jProton.selector.isIE && e1[0].selectSingleNode) {
      for(var i=0; ei=e1[i]; i++)
        ei.setAttribute("__diff", d);
    
      for(var i=0, rl=0; ei=e2[i]; i++)
        if(ei.getAttribute("__diff") != d)
          r[rl++] = ei;
    
     for(var i=0; ei=e1[i]; i++)
        ei.removeAttribute("__diff");
    
      return r;
    }
  
    for(var i = 0; i < l1; i++)
      e1[i].__diff = d;
  
    for(var i=0, rl=0; ei=e2[i]; i++)
      if(ei.__diff != d)
        r[rl++] = ei;
  
    return r;
  },
  
  /**
   * Compiles a selector/xpath query into a reusable function. The returned function
   * takes one parameter "root" (optional), which is the context node from where the query should start.
   * @param {String} selector The selector/xpath query
   * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
   * @return {Function}
   */
  compile: function(path, type){
    type = type || "select";
  
    var f = ["var fn = function(root){\n var mode; ++this.batch; var e = root || document;\n"],
        fl = 1, q = path, mode, lq,
        tk = this.matchers,
        tklen = tk.length, mm;

    // accept leading mode switch
    /*
    var lmode = q.match(this.reMode);
    if(lmode && lmode[1]){
      f[fl++] = 'mode="'+lmode[1].replace(this.reTrim, "")+'";';
      q = q.replace(lmode[1], "");
    }
    // strip leading slashes
    while(path.substr(0, 1)=="/")
      path = path.substr(1);
      /**/
  
    while(q && lq != q){
      lq = q;
      var tm = q.match(this.reSimple);
      if(type == "select"){
        if(tm){
          if(tm[1] == "#")
            f[fl++] = 'e = this.quickId("'+tm[2]+'");'
          else
            f[fl++] = 'e = this.getNodes(e, mode, "'+tm[2]+'");';
  
          q = q.replace(tm[0], "");
        }else if(q.substr(0, 1) != '@')
          f[fl++] = 'e = this.getNodes(e, mode, "*");';
  
      }else{
        if(tm)
          if(tm[1] == "#")
            f[fl++] = 'e = this.byId(e, "'+tm[2]+'");'
          else
            f[fl++] = 'e = this.byTag(e, "'+tm[2]+'");';
  
          q = q.replace(tm[0], "");
        }
  
      while(!(mm = q.match(this.reMode))){
        var matched = false;
        for(var j = 0; j < tklen; j++){
          var t = tk[j];
          var m = q.match(t.re);
          if(m){
            f[fl++] = t.select.replace(this.reTemplate, function(x, i){return m[i];});
            q = q.replace(m[0], "");
            matched = true;
            break;
          }
        }
        // prevent infinite loop on bad selector
        if(!matched)
          throw 'Error parsing selector, parsing failed at "' + q + '"';
      }
      if(mm[1]){
        f[fl++] = 'mode="'+mm[1].replace(this.reTrim, "")+'";';
        q = q.replace(mm[1], "");
      }
    }
    f[fl++] = "return this.unique(e);\n}";
    eval(f.join(""));
    return fn;
  }
}

/**
 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
 * @param {Array} el An array of elements to filter
 * @param {String} selector The simple selector to test
 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
 * the selector instead of the ones that match
 * @return {Array}
 */
jProton.collection.prototype.filter = function(els, ss, nonMatches){
  var selector = jProton.selector;
  ss = ss.replace(selector.reTrim, "");
  if(!selector.simpleCache[ss])
    selector.simpleCache[ss] = selector.compile(ss, "simple");

  var result = selector.simpleCache[ss].call(selector, els);
  return nonMatches ? selector.quickDiff(result, els) : result;
};

/**
 * Selects a group of elements.
 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
 * @param {Node} root (optional) The start of the query (defaults to document).
 * @return {Array}
 */
jProton.collection.prototype.select = function(path, root, type){
  var selector = jProton.selector;
  root = root || document;

  if(typeof root == "string")
    root = document.getElementById(root);

  var paths = path.split(",");
  //var results = [];
  for(var i = 0, len = paths.length; i < len; i++){
    var p = paths[i].replace(selector.reTrim, "");
    if(!selector.cache[p]){
      selector.cache[p] = selector.compile(p);
      if(!selector.cache[p])
        throw p + " is not a valid selector";
    }
    var result = selector.cache[p].call(selector, root);
    if(result && result != document)
      this.e = this.e.concat(result);
  }
  if(paths.length > 1)
    return selector.unique(this.e);
    
  return this;
};

// this eval is stop the compressor from
// renaming the variable to something shorter
eval("jProton.selector.batch = 30803;");